/*
 * HwndPainter.cpp
 *
 *  Created on: 2018年9月3日
 *      Author: zhengboyuan
 */

#include "HwndPainter.h"

HwndPainter::HwndPainter():
	m_paintContext(),
	m_scaleContext(),
	m_picture(),
	m_maxWidth(),
	m_maxHeight(),
	m_bmpInfo(),
	m_brush()
{
    memset(&m_picture, 0, sizeof(m_picture));

    memset(&m_bmpInfo, 0, sizeof(m_bmpInfo));
    m_bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    m_bmpInfo.bmiHeader.biCompression = BI_RGB;
    m_bmpInfo.bmiHeader.biBitCount = 32;
    m_bmpInfo.bmiHeader.biPlanes = 1;

	m_brush = CreateSolidBrush(RGB(0, 0, 0));
}

HwndPainter::~HwndPainter()
{
	reset();
}

void HwndPainter::reset()
{
    m_scaleContext.reset();

    avpicture_free(&m_picture);

    memset(&m_picture, 0, sizeof(m_picture));
    m_maxWidth = 0;
    m_maxHeight = 0;
}

void HwndPainter::setRatio(int ratioX, int ratioY)
{
	m_paintContext.ratioX = ratioX;
	m_paintContext.ratioY = ratioY;
}

bool HwndPainter::draw(HWND hwnd, HDC hdc, AVFrame* pFrame, HwndPaintProc proc, void* user)
{
	HDC winDC = NULL;
	if (hdc == NULL)
	{
		winDC = GetWindowDC(hwnd);
		hdc = winDC;
	}

	bool done = drawFrame(hwnd, hdc, pFrame, proc, user);

	if (winDC)
	{
		ReleaseDC(hwnd, winDC);
	}

	return done;
}

void HwndPainter::drawBackground(HWND hwnd, HDC hdc)
{
	HDC winDC = NULL;
	if (hdc == NULL)
	{
		winDC = GetWindowDC(hwnd);
		hdc = winDC;
	}

	RECT rc;
	::GetClientRect(hwnd, &rc);
	::FillRect(hdc, &rc, (HBRUSH)GetStockObject(BLACK_BRUSH));

	if (winDC)
	{
		ReleaseDC(hwnd, winDC);
	}
}

bool HwndPainter::drawFrame(HWND hwnd, HDC hdc, AVFrame* pFrame, HwndPaintProc proc, void* user)
{
	if (!pFrame || (pFrame->width <= 0))
	{
		return false;
	}

    RECT rcWin;
    BOOL ret = ::GetClientRect(hwnd, &rcWin);
    if (ret == FALSE)
    {
        return false;
    }

    if (IsRectEmpty(&rcWin))
    {
        return false;
    }

    bool success = false;
    RECT rcDest = getDestRect(pFrame->width, pFrame->height,
        rcWin.right - rcWin.left, rcWin.bottom - rcWin.top, m_paintContext.ratioX, m_paintContext.ratioY);
    if (IsRectEmpty(&rcDest))
    {
        return false;
    }

    int width = rcDest.right - rcDest.left;
    int height = rcDest.bottom - rcDest.top;
    if (m_scaleContext.update(pFrame, width, height))
    {
        resetPicture(width, height);
    }

    sws_scale(m_scaleContext.m_pContext, pFrame->data, pFrame->linesize, 0, pFrame->height,
        m_picture.data, m_picture.linesize);

//	if (m_textPainter.isEmpty())
//	{
//		drawPicture(hwnd, rcWin, rcDest, m_picture, pFrame);
//	}
//	else
	{
		drawPicture(hwnd, hdc, rcWin, rcDest, m_picture, pFrame, proc, user);

		//m_shouldErase = true;
	}

    return success;
}

void HwndPainter::resetPicture(int width, int height)
{
    if ((width > m_maxWidth) || (height > m_maxHeight))
    {
        avpicture_free(&m_picture);

        avpicture_alloc(&m_picture, AV_PIX_FMT_RGB32, width, height);
        int size = avpicture_get_size(AV_PIX_FMT_RGB32, width, height);
        memset(m_picture.data[0], 0x80, size);

        m_maxWidth = width;
        m_maxHeight = height;
    }
    else
    {
        m_picture.linesize[0] = width * 4;
    }
}

void HwndPainter::drawPicture(HWND hwnd, HDC hdc, const RECT& rcWin, const RECT& rc, AVPicture& pic, AVFrame* frame, HwndPaintProc proc, void* user)
{
	m_paintContext.hwnd = hwnd;
	m_paintContext.rcWin = rcWin;
	m_paintContext.rcDest = rc;
	m_paintContext.width = frame->width;
	m_paintContext.height = frame->height;
	m_paintContext.pts = (frame->pkt_dts != AV_NOPTS_VALUE) ? frame->pkt_dts : frame->pkt_pts;
	m_paintContext.pkt_pts = frame->pkt_pts;

	m_bmpInfo.bmiHeader.biWidth = rc.right - rc.left;
	m_bmpInfo.bmiHeader.biHeight = -(rc.bottom - rc.top);

	int winWidth = rcWin.right - rcWin.left;
	int winHeight = rcWin.bottom - rcWin.top;

	HDC hMemDC = CreateCompatibleDC(hdc);
	HBITMAP hMemBmp = CreateCompatibleBitmap(hdc, winWidth, winHeight);
	HBITMAP hOldBmp = (HBITMAP)SelectObject(hMemDC, hMemBmp);

	FillRect(hMemDC, &rcWin, m_brush);

	SetDIBitsToDevice(hMemDC,
		rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
		0, 0, 0, rc.bottom - rc.top,
		pic.data[0], &m_bmpInfo, 0);

	//BitBlt(hMemDC, 0, 0, winWidth, winHeight, hdc, 0, 0, SRCCOPY);

	m_paintContext.hdc = hMemDC;

	if (proc)
	{
		(*proc)(m_paintContext, frame, user);
	}

	BitBlt(hdc, 0, 0, winWidth, winHeight, hMemDC, 0, 0, SRCCOPY);

	SelectObject(hMemDC, hOldBmp);
	DeleteObject(hMemBmp);
	DeleteDC(hMemDC);

}

RECT HwndPainter::getDestRect(int picWidth, int picHeight,
                                int destWidth, int destHeight,
                                    int ratioX, int ratioY)
{
    RECT rc = {0, 0, destWidth, destHeight};
    if ((ratioX == 0) && (ratioY == 0))
    {
        // raw ratio
        rc = getScaleRect(rc, picWidth, picHeight);
    }
    else if ((ratioX < 0) || (ratioY < 0))
    {
        // fit window
    }
    else
    {
        rc = getScaleRect(rc, ratioX, ratioY);
    }
    return rc;
}

RECT HwndPainter::getScaleRect(const RECT& rc, int ratioX, int ratioY)
{
    RECT rcOut = rc;
    int rcWidth = rc.right - rc.left;
    int rcHeight = rc.bottom - rc.top;
    int diff = rcWidth * ratioY - rcHeight * ratioX;
    if (diff > 0)
    {
        rcOut.top = rc.top;
        rcOut.bottom = rc.bottom;
        int width = rcHeight * ratioX / ratioY;
        rcOut.left = rc.left + (rcWidth - width)/2;
        rcOut.right = rcOut.left + width;
    }
    else if (diff < 0)
    {
        rcOut.left = rc.left;
        rcOut.right = rc.right;
        int height = rcWidth * ratioY / ratioX;
        rcOut.top = rc.top + (rcHeight - height)/2;
        rcOut.bottom = rcOut.top + height;
    }
    return rcOut;
}
